package com.jfinal.bbsapp.sys;

import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.apache.log4j.Logger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.beust.jcommander.internal.Maps;
import com.google.common.base.Charsets;
import com.google.common.hash.Hashing;
import com.jfinal.bbsapp.common.kit.Base64Utils;
import com.jfinal.bbsapp.common.kit.CommonUtils;
import com.jfinal.bbsapp.common.kit.MyAnnotation;
import com.jfinal.bbsapp.common.kit.SendSmsal;
import com.jfinal.bbsapp.common.kit.SnowFlake;
import com.jfinal.core.JFinal;
import com.jfinal.kit.HashKit;
import com.jfinal.kit.PathKit;
import com.jfinal.kit.PropKit;
import com.jfinal.kit.Ret;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.redis.Cache;
import com.jfinal.plugin.redis.Redis;
import com.jfinal.bbsapp.common.kit.FileUtils;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

@MyAnnotation(memo="用户业务类")
public class SysService {
	
	private final Logger logger = Logger.getLogger(SysService.class);
	private static SnowFlake snowFlake = new SnowFlake(1, 1);
	private static String appId = PropKit.get("appId"); //腾讯人脸核身应用ID
	
	@MyAnnotation(memo="发送验证码")
	public Ret sendmsg(String mobile,String ischeckm) {
		if(StrKit.notBlank(ischeckm) && ischeckm.equals("1")){
			Record rec = Db.findFirst("select uid from t_user where flag='0' and mobile=?", mobile);
			if (rec == null) {
				return Ret.fail("手机号不存在").set("code", 51000);
			}
	    }
		
		try {
			Cache rds = Redis.use();
			Set<String> keys = rds.keys(mobile);
			if (keys.size() > 0) {
				for (String key : keys) {
					rds.del(key);
				}
			}
			//rds.setex(mobile, 600, "000000");
			//return Ret.ok("data", "000000").set("code", 20000);
			
			SendSmsal sendSms = new SendSmsal();
			int randomInt = sendSms.randomInt();
			String check = sendSms.sendSms(mobile, String.valueOf(randomInt),"10");
			if (!"".equals(check)) {
				rds.setex(mobile, 600, String.valueOf(randomInt));
				return Ret.ok("data", "发送验证码成功！").set("code", 20000);
			} else {
				return Ret.fail("发送验证码失败！").set("code", 51000);
			}
		} catch (Exception e) {
			logger.error("------Exception------", e);
			return Ret.fail("系统异常").set("code", 50000);
		}
	}
	
	@MyAnnotation(memo="手机号注册")
	public Ret reg(String mobile,String mobilecode) {
		try {
			Record rec = Db.findFirst("select userid from c_user where flag <> '9' and mobile=?",mobile);
			if (rec != null) {
				return Ret.fail("此手机号已注册过").set("code", 51000);
			}
			
			Cache rds = Redis.use();
			String mobile_code = rds.get(mobile);
			if (("".equals(mobile_code)) || (mobile_code == null)) {
				return Ret.fail("验证码已失效").set("code", 51000);
			}
			if (!mobile_code.equals(mobilecode)) {
				return Ret.fail("验证码错误，请重新输入").set("code", 51000);
			}
			
			long userid = snowFlake.nextId();
			//String orderNo= UUID.randomUUID().toString().replace("-", "");
			
			// 密码加盐 hash
			String salt = HashKit.generateSaltForSha256();
			String password = HashKit.sha256(salt + mobile.substring(mobile.length()-6));
			Db.update("insert into c_user(userid,mobile,nickname,pwd,salt,ctime) values (?,?,?,?,?,?)",
			userid,mobile,"",password,salt,System.currentTimeMillis());
			return Ret.ok("userid", String.valueOf(userid)).set("flag", "0").set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常").set("code", 50000);	
		}		
	}
	
	@MyAnnotation(memo="登录")
	public Ret login(String mobile, String password, String mobilecode) {
		try {
			Record rec = Db.findFirst("select cast(userid as char) userid,mobile,nickname,pwd,salt,headimg,faceId,flag"
				+ " from c_user where flag <> '9' and mobile=?",mobile);
			if (rec == null) {
				return Ret.fail("此用户尚未注册过").set("code", 51000);
			}
			// 通过验证码登陆
			boolean codechecked = false;
			if(StrKit.notBlank(mobilecode)){
				Cache rds = Redis.use();
				String mobile_code = rds.get(mobile);
				if (("".equals(mobile_code)) || (mobile_code == null)) {
					return Ret.fail("验证码已失效").set("code", 51000);
				}
				if (!mobile_code.equals(mobilecode)) {
					return Ret.fail("验证码错误，请重新输入").set("code", 51000);
				}
				codechecked = true;
		    }
			
			if (!codechecked && (StrKit.notBlank(password) && !rec.getStr("pwd").equals(HashKit.sha256(rec.getStr("salt")+password)))) {
				return Ret.fail("密码错误，请重新输入").set("code", 51000);
			}
			rec.remove("pwd");
			rec.remove("salt");
			
			// 生成TOKEN并记录到缓存
			String token = UUID.randomUUID().toString().replace("-", "");
			rec.set("token", token);
			Map<Object, Object> data = Maps.newHashMap();
			for(String key : rec.getColumns().keySet()){
				data.put(key, rec.get(key));
			}

			Cache rds = Redis.use();
			Set<String> keys = rds.keys("app-*-" + rec.get("userid"));
			if (keys.size() > 0) {//删除可能存在的登录
				for (String key : keys) {
					rds.del(key);
				}
			}
			rds.hmset("app-" + token + "-"  + rec.get("userid"), data);
			return Ret.ok("data", data).set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);
		}
	}
	
	@MyAnnotation(memo="找回密码")
	public Ret findpwd(String mobile, String code, String password) {
		try {
			Record rec = Db.findFirst("select userid from c_user where flag <> '9' and mobile=?",mobile);
			if (rec == null) {
				return Ret.fail("此用户尚未注册过").set("code", 51000);
			}
			//校验验证码
			Cache rds = Redis.use();
			String mobile_code = rds.get(mobile);
			if (("".equals(mobile_code)) || (mobile_code == null)) {
				return Ret.fail("验证码已失效").set("code", 51000);
			}
			if (!mobile_code.equals(code)) {
				return Ret.fail("验证码错误，请重新输入").set("code", 51000);
			}
			// 密码加盐 hash
			String salt = HashKit.generateSaltForSha256();
			String newpwd = HashKit.sha256(salt + password);
			Db.update("update c_user set salt=?,pwd=? where userid=?",salt,newpwd,rec.getStr("userid"));
			return Ret.ok("提交成功").set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:", e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);
		}
	}
	
	@MyAnnotation(memo="获取appid")
	public Ret getappid(String token,String realname,String sourcephotostr) {
		try {
			String userid=CommonUtils.getuseridbytoken(token);
			Record urec = Db.findFirst("select faceimg,flag,orderno from c_user where userid=?",userid);
			if (urec == null) {
				return Ret.fail("此用户尚未注册过");
			}
			
			String faceId = "";
			String orderno = UUID.randomUUID().toString().replace("-", "");
			//注册完后登录开课第一次核身图片
			if(urec.getStr("flag").equals("0")) {
				faceId=this.getfaceId(userid,orderno,sourcephotostr);
				//核身照片存储为下次使用
				String faceimg = sourcephotostr;
				if (faceimg.contains("data:")) {
				    int start = faceimg.indexOf(",");
				    faceimg = faceimg.substring(start + 1);
				}
				faceimg = faceimg.replaceAll("\r|\n", "");faceimg = faceimg.trim();
				byte[] imageData = Base64.getMimeDecoder().decode(faceimg);
				
				Date currentTime = new Date();
				SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
				String dateString = formatter.format(currentTime);
				String path = PathKit.getWebRootPath();
				path = path + "/" + JFinal.me().getConstants().getBaseUploadPath() + "/" + dateString + "/";
				String name = UUID.randomUUID().toString().replace("-", "") + ".jpeg";
				File file = new File(path);//创建文件夹
			    if (!file.exists() && !file.isDirectory()) {
			    	file.mkdir();
			    }
				try (FileOutputStream write = new FileOutputStream(new File(path + name))) {
					write.write(imageData);	
				}
				//上传七牛
				faceimg = FileUtils.upload(path,name);
				Db.update("update c_user set faceId=?,faceimg=?,realname=? where userid=?",faceId,faceimg,realname,userid);
			}
			//第二次核身图片微信人脸核身不存储人脸图片，每次核身前要再次上传已经核对过的人脸图片并得到新的参数faceId
			if(urec.getStr("flag").equals("1")) {
				//拉图片传入
				//String faceimg = "data:image/jpeg;base64," + Base64Utils.imgBase64(urec.getStr("faceimg"));
				String faceimg = Base64Utils.imgBase64(urec.getStr("faceimg"));
				//System.out.println(faceimg);
				faceId=this.getfaceId(userid,orderno,faceimg);
				Db.update("update c_user set faceId=? where userid=?",faceId,userid);
			}
			////获取NONCEticket用于前端sdk获取
			String nonce= UUID.randomUUID().toString().replace("-", "");
			String tickets="";String sign="";
			Cache rds = Redis.use();
			String access_token = rds.get("wx-access_token");
			OkHttpClient client = new OkHttpClient();
			String url = "https://kyc1.qcloud.com/api/oauth2/api_ticket?appId=" + appId + "&access_token=" + access_token + "&type=NONCE&version=1.0.0&user_id="+userid;
			Request request = new Request.Builder().url(url).get().build();
			Response response = client.newCall(request).execute();
			ResponseBody responseBody = response.body();
		    if (responseBody != null) {
		        String res = responseBody.string();
		        JSONObject resobj = JSON.parseObject(res);
		        String code = resobj.getString("code");
				if(code.equals("0")) {
					JSONArray firstArray = resobj.getJSONArray("tickets");
					JSONObject jsonObjecttickets = firstArray.getJSONObject(0);
					tickets = jsonObjecttickets.getString("value");
					//System.out.println("tickets------>"+tickets);
				}
		    }
		    if(StrKit.notBlank(tickets)) {
		    	List<String> list = new ArrayList<>();
				Collections.addAll(list, appId,userid,"1.0.0",nonce);
				sign = sign(list,tickets);	
		    }
			return Ret.ok("faceId",faceId).set("agreementNo",orderno).set("openApiAppId",appId).set("openApiNonce",nonce).set("openApiSign",sign).set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);
		}
	}
	
	@MyAnnotation(memo="合作方后台上传身份信息(人脸图片)获取faceId")
	public String getfaceId(String userid,String orderNo,String sourcephotostr) {
		String faceId = "";
		try {
			String nonce= UUID.randomUUID().toString().replace("-", "");
			OkHttpClient client = new OkHttpClient();
			String url = "https://kyc1.qcloud.com/api/server/getAdvFaceId?orderNo=" + orderNo;
			JSONObject params = new JSONObject();
			params.put("orderNo", orderNo);
			params.put("appId", appId);
			params.put("userId", userid);
			params.put("version", "1.0.0");
			Cache rds = Redis.use();
			String signticket = rds.get("wx-SIGN-tickets");
			params.put("ticket", signticket);
			params.put("nonce", nonce);
			
			List<String> list = new ArrayList<>();
			Collections.addAll(list, appId,userid,"1.0.0",nonce);
			String sign = sign(list,signticket);
			params.put("sign",sign);
			params.put("sourcePhotoType", "2");
			params.put("sourcePhotoStr", sourcephotostr);
			
			MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
	        RequestBody body =  RequestBody.create(mediaType,params.toString());
	        Request request = new Request.Builder().url(url).post(body).build();
			try (Response response = client.newCall(request).execute()) {
			    ResponseBody responseBody = response.body();
			    if (responseBody != null) {
			        String res = responseBody.string();
			        JSONObject resobj = JSON.parseObject(res);
			        String code = resobj.getString("code");
					if(code.equals("0")) {
						JSONObject jsonbody = JSONObject.parseObject(resobj.getString("result"));
						faceId = jsonbody.getString("faceId");
						Db.update("update c_user set orderno=? where userid=?",orderNo,userid);
						//System.out.println("faceId:------>"+faceId);
					}
			    }
			}catch (Exception e) {
				logger.error("Exception:" , e);	
			}
		} catch (Exception e) {
			logger.error("Exception:" , e);	
		}
		return faceId;
	}
	
	@MyAnnotation(memo="生成签名算法")
	public static String sign(List<String> values, String ticket) { //values传ticket外的其他参数
		if (values == null) {
			throw new NullPointerException("values is null");
		}
		values.removeAll(Collections.singleton(null));
		values.add(ticket);
		java.util.Collections.sort(values);
		StringBuilder sb = new StringBuilder();
		for (String s : values) {
			sb.append(s);
		}
		return Hashing.sha1().hashString(sb,Charsets.UTF_8).toString().toUpperCase();
	}
	
	@MyAnnotation(memo="用户注册完第一次人脸核身成功结果更新")
	public Ret fchecksuccess(String token) {
		String userid=CommonUtils.getuseridbytoken(token);
		Record rec = Db.findFirst("select orderno from c_user where userid=?",userid);
		if (rec == null) {
			return Ret.fail("此用户不存在");
		}
		try {
			Db.update("update c_user set flag='1',checktime=? where userid=?",System.currentTimeMillis(),userid);
			CommonUtils.uptoken(token);
			return Ret.ok("flag", "1").set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);
		}
		
		//线程休眠
		//TimeUnit.SECONDS.sleep(5);
		//查询核身结果拉取照片:=====>https://cloud.tencent.com/document/product/1007/57638
		/*
		String photo = "";
		OkHttpClient client = new OkHttpClient();
		String url = "https://kyc1.qcloud.com/api/v2/base/queryfacerecord?orderNo=" + rec.getStr("orderno");
		JSONObject params = new JSONObject();
		params.put("appId", appId);
		params.put("orderNo", rec.getStr("orderno"));
		params.put("version", "1.0.0");
		Cache rds = Redis.use();
		String signticket = rds.get("wx-SIGN-tickets");
		params.put("ticket", signticket);
		String nonce= UUID.randomUUID().toString().replace("-", "");
		params.put("nonce", nonce);
		
		List<String> list = new ArrayList<>();
		Collections.addAll(list, appId,rec.getStr("orderno"),"1.0.0",nonce);
		String sign = sign(list,signticket);
		params.put("sign",sign);
		//是否需要获取人脸识别的视频和文件，值为1则返回视频和照片、值为2则返回照片、值为3则返回视频；其他则不返回
		params.put("getFile","2");
		
		MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
        RequestBody body =  RequestBody.create(mediaType,params.toString());
        Request request = new Request.Builder().url(url).post(body).build();
		try (Response response = client.newCall(request).execute()) {
		    ResponseBody responseBody = response.body();
		    if (responseBody != null) {
		        String res = responseBody.string();
		        System.out.println("res:------>"+res);
		        JSONObject resobj = JSON.parseObject(res);
		        String code = resobj.getString("code");
				if(code.equals("0")) {
					JSONObject jsonbody = JSONObject.parseObject(resobj.getString("result"));
					photo = jsonbody.getString("photo");
			        System.out.println("photo:------>"+photo);
				}else {
					return Ret.fail(code+":"+resobj.getString("msg")).set("code", 50000);		
				}
		    }else {
		    	return Ret.fail("接口调用返回失败,请稍后重试").set("code", 50000);	
		    }
		}catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);	
		}
		String faceimg = "";
		if(StrKit.notBlank(photo)) {
			byte[] imageData = null;
			imageData= Base64.getDecoder().decode(photo);
            if(imageData.length > 0) {
            	String name = UUID.randomUUID().toString().replace("-", "") + ".jpg";
 				faceimg = FileUtils.upload(imageData,name);
 				Db.update("update c_user set faceimg=? where userid=?",faceimg,userid);
			}
		}*/
	}
	
	@MyAnnotation(memo="用户第二次以及以后N次人脸核身结果更新")
	public Ret facecheck(String token,String flag) {
		try {
			String userid=CommonUtils.getuseridbytoken(token);
			if(flag.equals("0")){//当前人是否需要人脸核身
				Db.update("update c_user set flag=? where userid=?",flag,userid);
			}else {
				Db.update("update c_user set flag=?,checktime=? where userid=?",flag,System.currentTimeMillis(),userid);
			}
			CommonUtils.uptoken(token);
			return Ret.ok("flag", flag).set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);
		}
	}
	
	@MyAnnotation(memo="获取个人信息")
	public Ret getuser(String token) {
		try {
			String userid=CommonUtils.getuseridbytoken(token);
			Record rec = Db.findFirst("select cast(userid as char) userid,mobile,nickname,headimg,faceId,flag"
				+ " from c_user where userid=?", userid);
			return Ret.ok("data", rec).set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);
		}
	}
	
	@MyAnnotation(memo="根据用户id查询用户信息")
	public Ret getuserbyid(String token,String userid) {
		try {
			Record rec = Db.findFirst("select cast(userid as char) userid,mobile,nickname,headimg,faceId,flag"
					+ " from c_user where userid=?", userid);
			return Ret.ok("data", rec).set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);
		}
	}
	
	@MyAnnotation(memo="我的推荐人")
	public Ret mypre(String token) {
		try {
			String userid=CommonUtils.getuseridbytoken(token);
			Record urec = Db.findFirst("select serid from c_user where userid=?", userid);
			Record prerec = null;
			if(urec != null && urec.getLong("serid") > 0) {
				prerec = Db.findFirst("select username,mobile from sys_user where id=?", urec.getLong("serid"));	
			}
			return Ret.ok("data", prerec).set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);
		}
	}
	
	@MyAnnotation(memo="更新头像")
	public Ret upheadimg(String token, String headimg) {
		try {
			String userid=CommonUtils.getuseridbytoken(token);
			Db.update("update c_user set headimg=? where userid=?",headimg,userid);
			CommonUtils.uptoken(token);
			return Ret.ok("提交成功").set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);
		}
	}
	
	@MyAnnotation(memo="更新昵称")
	public Ret upnickname(String token, String nickname) {
		try {
			String userid=CommonUtils.getuseridbytoken(token);
			Db.tx(() -> {
				Db.update("update c_user set nickname=? where userid=?",nickname,userid);
				return true;
			});	
			CommonUtils.uptoken(token);
			return Ret.ok("提交成功").set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);
		}
	}
	
	@MyAnnotation(memo="同时更新头像与昵称")
	public Ret upheadnick(String token, String headimg, String nickname) {
		try {
			String userid=CommonUtils.getuseridbytoken(token);
			Db.update("update c_user set headimg=?,nickname=? where userid=?",headimg,nickname,userid);
			CommonUtils.uptoken(token);
			return Ret.ok("提交成功").set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);
		}
	}
	
	@MyAnnotation(memo="更新用户手机号")
	public Ret upmobile(String token, String mobile) {
		try {
			String userid=CommonUtils.getuseridbytoken(token);
			Db.update("update b_user set mobile=? where userid=?",mobile,userid);
			CommonUtils.uptoken(token);
			return Ret.ok("提交成功").set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);	
		}
	}
	
	@MyAnnotation(memo="获取版本信息")
	public Ret getversion(String clienttype,String systype) {
		//clienttype:0用户端1配送端2Pos端/systype:0-Android1-IOS
		try {
			Record rec = Db.findFirst("select vcode,vname,url,memo,isupdate from sys_version where clienttype=? and systype=? order by ctime desc",clienttype, systype);
			return Ret.ok("data", rec).set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);	
		}
	}
	
	@MyAnnotation(memo="修改登录密码")
	public Ret updpwd(String userid, String oldpwd, String pwd) {
		try {
			Record rec = Db.findFirst("select cast(userid as char) userid,pwd,salt from c_user where userid=?",userid);
			if (rec == null || (!rec.getStr("pwd").equals(HashKit.sha256(rec.getStr("salt") + oldpwd)))) {
				return Ret.fail("原密码错误").set("code", 51000);
			}
			String salt = HashKit.generateSaltForSha256();//密码加盐 hash
			String newpwd = HashKit.sha256(salt + pwd);
			Db.update("update c_user set salt=?,pwd=? where userid=?",salt,newpwd,userid);
			return Ret.ok("提交成功").set("code", 20000);
		} catch (Exception e) {
			logger.error("Exception:" , e);
			return Ret.fail("系统异常请稍后重试").set("code", 50000);	
		}
	}
}
